home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / WASTE 1.2 Distribution / WASTE 1.2 / WEMouse.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-18  |  31.1 KB  |  1,233 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WEMouse.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Mouse Clicks and Support for Drag and Drop
  6.  *
  7.  *  Copyright (c) 1993-1996 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. #ifndef __FOLDERS__
  18. #include <Folders.h>
  19. #endif
  20.  
  21. #if WASTE_IC_SUPPORT
  22. #ifndef __ICTYPES__
  23. #include "ICTypes.h"
  24. #endif
  25. #ifndef __ICAPI__
  26. #include "ICAPI.h"
  27. #endif
  28. #endif // WASTE_IC_SUPPORT
  29.  
  30. // static variables
  31.  
  32. static DragSendDataUPP _weFlavorSender = nil;
  33.  
  34. pascal Boolean _WEIsOptionDrag(DragReference drag)
  35. {
  36.     EventModifiers downModifiers, upModifiers;
  37.  
  38.     // get drag modifiers
  39.     GetDragModifiers(drag, nil, (SInt16 *) &downModifiers, (SInt16 *) &upModifiers);
  40.  
  41.     // return true if the option key was held down at the beginning and/or at the end
  42.     return (((downModifiers | upModifiers) & optionKey) != 0);
  43.  
  44. }
  45.  
  46. pascal OSErr _WEGetFlavor(DragReference drag, ItemReference dragItem,
  47.                 FlavorType requestedType, Handle hFlavor,
  48.                 SInt32 dropOffset, WEHandle hWE)
  49. {
  50.  
  51.     // get the requested flavor out of the specified drag reference and put it into
  52.     // the given handle, if any -- if hFlavor is nil, just check whether the specified flavor
  53.     // is there or can be obtained by invoking a user-defined translation routine
  54.  
  55.     FlavorFlags theFlags;
  56.     Size theSize;
  57.     WETranslateDragUPP translateDragHook;
  58.     Boolean saveFlavorLock;
  59.     OSErr err;
  60.  
  61.     // see if the drag item has the requested flavor type,
  62.     // without forcing the actual data to be sent and/or translated
  63.     err = GetFlavorFlags(drag, dragItem, requestedType, &theFlags);
  64.     if (err == badDragFlavorErr)
  65.     {
  66.         // requested flavor is not available: our client may try a custom translation
  67.         // this is especially handy to translate HFS objects like TEXT and PICT files
  68.         if ((translateDragHook = (*hWE)->translateDragHook) != nil)
  69.         {
  70.             err = CallWETranslateDragProc(drag, dragItem, requestedType, hFlavor,
  71.                         dropOffset, hWE, translateDragHook);
  72.         }
  73.     }
  74.     else if (err == noErr)
  75.     {
  76.  
  77.         // requested flavor is available: get it if hFlavor is not nil
  78.         if (hFlavor == nil)
  79.             return err;
  80.  
  81.         // get size of flavor data
  82.         if ((err = GetFlavorDataSize(drag, dragItem, requestedType, &theSize)) != noErr)
  83.             return err;
  84.  
  85.         // resize the handle
  86.         SetHandleSize(hFlavor, theSize);
  87.         if ((err = MemError()) != noErr)
  88.             return err;
  89.  
  90.         // get flavor data
  91.         saveFlavorLock = _WESetHandleLock(hFlavor, true);
  92.         err = GetFlavorData(drag, dragItem, requestedType, *hFlavor, &theSize, 0);
  93.         _WESetHandleLock(hFlavor, saveFlavorLock);
  94.     }
  95.  
  96.     return err;
  97. }
  98.  
  99. pascal OSErr _WEExtractFlavor(DragReference drag, ItemReference dragItem,
  100.                         FlavorType theType, Handle *hFlavor,
  101.                         SInt32 dropOffset, WEHandle hWE)
  102. {
  103.     OSErr err;
  104.  
  105.     // allocate a new handle
  106.     if ((err = _WEAllocate(0, kAllocTemp, hFlavor)) == noErr)
  107.     {
  108.         // put the requested flavor into this handle
  109.         if ((err = _WEGetFlavor(drag, dragItem, theType, *hFlavor, dropOffset, hWE)) != noErr)
  110.         {
  111.             // if an error occurred, forget the handle
  112.             _WEForgetHandle(hFlavor);
  113.         }
  114.     }
  115.  
  116.     return err;
  117. }
  118.  
  119. pascal Boolean WECanAcceptDrag(DragReference drag, WEHandle hWE)
  120. {
  121.     WEPtr pWE;
  122.     UInt16 numDragItems;
  123.     UInt16 dragItemIndex;
  124.     ItemReference dragItem;
  125.     Boolean saveWELock;
  126.     OSErr err;
  127.     Boolean retval = false;
  128.  
  129.     // lock the WE record
  130.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  131.     pWE = *hWE;
  132.  
  133.     // refuse all drags if the weFReadOnly feature is enabled
  134.     if (BTST(pWE->features, weFReadOnly))
  135.         goto cleanup;
  136.  
  137.     // count items in this drag
  138.     if ((err = CountDragItems(drag, &numDragItems)) != noErr)
  139.         goto cleanup;
  140.  
  141.     for (dragItemIndex = 1; dragItemIndex <= numDragItems; dragItemIndex++)
  142.     {
  143.         // get item reference number for current drag item
  144.         if ((err = GetDragItemReferenceNumber(drag, dragItemIndex, &dragItem)) != noErr)
  145.             goto cleanup;
  146.  
  147.         // see if this drag item contains a text flavor
  148.         err = _WEGetFlavor(drag, dragItem, kTypeText, nil, kInvalidOffset, hWE);
  149.  
  150. #if WASTE_OBJECTS
  151.         if (err == badDragFlavorErr)
  152.         {
  153.             SInt32 objectIndex;
  154.             FlavorType objectType;
  155.  
  156.             // see if this drag item contains a flavor matching one of the registered object types
  157.             objectIndex = 0;
  158.             while (_WEGetIndObjectType(objectIndex, &objectType, hWE) == noErr)
  159.             {
  160.                 err = _WEGetFlavor(drag, dragItem, objectType, nil, kInvalidOffset, hWE);
  161.                 if (err != badDragFlavorErr)
  162.                     break;  // enclosing while
  163.                 objectIndex++;
  164.             } // while
  165.         }
  166. #endif
  167.  
  168.         if (err != noErr)
  169.             goto cleanup;
  170.     } // for
  171.  
  172.     retval = true;
  173.  
  174. cleanup:
  175.  
  176.     // unlock the WE record
  177.     _WESetHandleLock((Handle) hWE, saveWELock);
  178.  
  179.     return retval;
  180.  
  181. }
  182.  
  183. pascal void _WEUpdateDragCaret(SInt32 offset, WEHandle hWE)
  184. {
  185.     WEPtr pWE = *hWE;    // assume WE record is already locked
  186.     UInt32 currentTime;
  187.  
  188.     // get current time
  189.     currentTime = TickCount();
  190.  
  191.     if (offset == pWE->dragCaretOffset)
  192.     {
  193.  
  194.         // drag caret offset didn't change; blink the caret
  195.         if ((currentTime > pWE->caretTime + GetCaretTime()) && (offset != kInvalidOffset))
  196.         {
  197.             _WEDrawCaret(pWE->dragCaretOffset, hWE);
  198.             BCHG(pWE->flags, weFDragCaretVisible);    // invert flag
  199.             pWE->caretTime = currentTime;
  200.         }
  201.     }
  202.     else
  203.     {
  204.  
  205.         // drag caret offset did change
  206.         // hide old caret, if it's showing
  207.         if (BTST(pWE->flags, weFDragCaretVisible))
  208.             _WEDrawCaret(pWE->dragCaretOffset, hWE);
  209.  
  210.         // show new caret (unless offset is kInvalidOffset)
  211.         if (offset != kInvalidOffset)
  212.         {
  213.             _WEDrawCaret(offset, hWE);
  214.             BSET(pWE->flags, weFDragCaretVisible);
  215.             pWE->caretTime = currentTime;
  216.         }
  217.         else
  218.         {
  219.             BCLR(pWE->flags, weFDragCaretVisible);
  220.         }
  221.  
  222.         // remember drag caret offset
  223.         pWE->dragCaretOffset = offset;
  224.     }
  225. }
  226.  
  227. pascal OSErr WETrackDrag(DragTrackingMessage message, DragReference drag, WEHandle hWE)
  228. {
  229.     WEPtr pWE;
  230.     DragAttributes attributes;
  231.     Point mouse;
  232.     LongPt thePoint;
  233.     SInt32 offset;
  234.     UInt32 currentTime;
  235.     WEEdge edge;
  236.     Boolean saveWELock;
  237.     OSErr err;
  238.  
  239.     // lock the WE record
  240.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  241.     pWE = *hWE;
  242.  
  243.     // dispatch on message
  244.     switch(message)
  245.     {
  246.         case dragTrackingEnterWindow:
  247.             // determine whether we can accept this drag
  248.             if (WECanAcceptDrag(drag, hWE))
  249.                 BSET(pWE->flags, weFCanAcceptDrag);
  250.             else
  251.                 BCLR(pWE->flags, weFCanAcceptDrag);
  252.  
  253.             // reset clickTime
  254.             pWE->clickTime = 0;
  255.             break;
  256.  
  257.         case dragTrackingInWindow:
  258.             if (BTST(pWE->flags, weFCanAcceptDrag))
  259.             {
  260.  
  261.                 // get drag attributes
  262.                 if ((err = GetDragAttributes(drag, &attributes)) != noErr)
  263.                     goto cleanup;
  264.  
  265.                 // get current mouse location in local coordinates
  266.                 if ((err = GetDragMouse(drag, &mouse, nil)) != noErr)
  267.                     goto cleanup;
  268.                 GlobalToLocal(&mouse);
  269.  
  270.                 if (PtInRgn(mouse, pWE->viewRgn))
  271.                 {
  272.                     // mouse is in text area
  273.                     // hilite the text rectangle, if we haven't already
  274.                     // and if the drag has left sender window since drag tracking started
  275.                     if ((!BTST(pWE->flags, weFHilited)) &&
  276.                         (attributes & dragHasLeftSenderWindow) != 0)
  277.                     {
  278.                         CallWEHiliteDropAreaProc(drag, true, hWE, pWE->hiliteDropAreaHook);
  279.                         BSET(pWE->flags, weFHilited);
  280.                     }
  281.  
  282.                     // hide the caret
  283.                     if (BTST(pWE->flags, weFCaretVisible))
  284.                         _WEBlinkCaret(hWE);
  285.  
  286.                     // get text offset corresponding to mouse location
  287.                     WEPointToLongPoint(mouse, &thePoint);
  288.                     offset = WEGetOffset(&thePoint, &edge, hWE);
  289.  
  290.                     // if offset is within the original selection range, don't display drag feedback
  291.                     if (drag == pWE->currentDrag)
  292.                         if (_WEOffsetInRange(offset, edge, pWE->selStart, pWE->selEnd))
  293.                             offset = kInvalidOffset;
  294.  
  295.                     // provide a drag feedback in the form of a blinking caret
  296.                     _WEUpdateDragCaret(offset, hWE);
  297.  
  298.                     // clear clickTime
  299.                     pWE->clickTime = 0;
  300.                 }
  301.                 else
  302.                 {
  303.  
  304.                     // mouse is outside text area
  305.                     // dehilite the text rectangle, if it's hilited
  306.                     if (BTST(pWE->flags, weFHilited))
  307.                     {
  308.                         CallWEHiliteDropAreaProc(drag, false, hWE, pWE->hiliteDropAreaHook);
  309.                         BCLR(pWE->flags, weFHilited);
  310.                     }
  311.  
  312.                     // hide the drag caret, if it's showing
  313.                     _WEUpdateDragCaret(kInvalidOffset, hWE);
  314.  
  315.                     // if the mouse has been remaining outside the view region for 10 ticks or more
  316.                     // and this drag was created by this WE instance, call the click loop routine
  317.                     if (drag == pWE->currentDrag)
  318.                     {
  319.                         currentTime = TickCount();
  320.                         if (pWE->clickTime == 0)
  321.                             pWE->clickTime = currentTime;
  322.                         else if (currentTime > pWE->clickTime + kAutoScrollDelay)
  323.                             if (pWE->clickLoop != nil)
  324.                                 CallWEClickLoopProc(hWE, pWE->clickLoop);
  325.                     }
  326.                 }
  327.             }
  328.             break; // case dragTrackingInWindow
  329.  
  330.         case dragTrackingLeaveWindow:
  331.  
  332.             // drag has left this window
  333.             // dehilite the text area if necessary
  334.             if (BTST(pWE->flags, weFHilited))
  335.             {
  336.                 CallWEHiliteDropAreaProc(drag, false, hWE, pWE->hiliteDropAreaHook);
  337.                 BCLR(pWE->flags, weFHilited);
  338.             }
  339.  
  340.             // hide the drag caret, if it's showing
  341.             _WEUpdateDragCaret(kInvalidOffset, hWE);
  342.  
  343.             break;
  344.  
  345.         default:
  346.             break;
  347.     }
  348.  
  349.     // clear result code
  350.     err = noErr;
  351.  
  352. cleanup:
  353.     // unlock the WE record
  354.     _WESetHandleLock((Handle) hWE, saveWELock);
  355.  
  356.     // return result code
  357.     return err;
  358.  
  359. }
  360.  
  361. pascal OSErr WEReceiveDrag(DragReference drag, WEHandle hWE)
  362. {
  363.     WEPtr pWE;
  364.     WEActionHandle hAction;
  365.     Handle hText = nil;
  366.     Handle hStyles = nil;
  367. #if WASTE_OBJECTS
  368.     Handle hSoup = nil;
  369. #endif
  370.     Handle hObjectData = nil;
  371.     Point mouse;
  372.     LongPt dropLocation;
  373.     SInt32 insertionOffset;
  374.     SInt32 insertionLength;
  375.     SInt32 sourceStart;
  376.     SInt32 sourceEnd;
  377.     SInt32 destStart;
  378.     SInt32 destEnd;
  379.     SInt32 delta;
  380.     UInt16 dragItemIndex;
  381.     UInt16 numDragItems;
  382.     ItemReference dragItem;
  383.     GrafPtr savePort;
  384.     SInt16 intPasteAction;
  385.     WEEdge dropEdge;
  386.     char space = kSpace;
  387.     Boolean isMove = false;
  388.     Boolean isBackwards;
  389.     Boolean saveWELock;
  390.     OSErr err;
  391.  
  392.     // lock the WE record
  393.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  394.     pWE = *hWE;
  395.  
  396.     // set up the port
  397.     GetPort(&savePort);
  398.     SetPort(pWE->port);
  399.  
  400.     // stop any ongoing inline input session
  401.     WEStopInlineSession(hWE);
  402.  
  403.     // hide the drag caret
  404.     _WEUpdateDragCaret(kInvalidOffset, hWE);
  405.  
  406.     // refuse this drag if it doesn't taste good
  407.     err = badDragFlavorErr;
  408.     if (!WECanAcceptDrag(drag, hWE))
  409.         goto cleanup;
  410.  
  411.     // get drop location in local coordinates
  412.     if ((err = GetDragMouse(drag, &mouse, nil)) != noErr)
  413.         goto cleanup;
  414.     GlobalToLocal(&mouse);
  415.  
  416.     // for the drag to be accepted, the drop location must be within the view region
  417.     err = dragNotAcceptedErr;
  418.     if (!PtInRgn(mouse, pWE->viewRgn))
  419.         goto cleanup;
  420.  
  421.     // get drop offset into the text
  422.     WEPointToLongPoint(mouse, &dropLocation);
  423.     insertionOffset = WEGetOffset(&dropLocation, &dropEdge, hWE);
  424.     // destStart/destEnd define the range to highlight at the end of the drag
  425.     destStart = insertionOffset;
  426.  
  427.     // drag originated from this same window?
  428.     if (drag == pWE->currentDrag)
  429.     {
  430.  
  431.         // sourceStart/sourceEnd define the range to delete at the end of the move
  432.         sourceStart = pWE->selStart;
  433.         sourceEnd = pWE->selEnd;
  434.  
  435.         // remember text length before insertion
  436.         delta = pWE->textLength;
  437.  
  438.         // if insertion offset is within the original selection range, abort the drag
  439.         // (*err = dragNotAcceptedErr;*)
  440.         if (_WEOffsetInRange(insertionOffset, dropEdge, sourceStart, sourceEnd))
  441.             goto cleanup;
  442.  
  443.         // if the drag originated from this window, a move,
  444.         // rather than a copy, should be performed
  445.         // Exception: the option key may be held down at mouse-down
  446.         // or mouse-up time to force a copy operation.
  447.  
  448.         isMove = !_WEIsOptionDrag(drag);
  449.         isBackwards = (insertionOffset <= sourceStart);
  450.     } // if intra-window drag
  451.  
  452.     // clear null style
  453.     BCLR(pWE->flags, weFUseNullStyle);
  454.  
  455.     // hide selection highlighting
  456.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  457.  
  458.     // increment modification count
  459.     pWE->modCount++;
  460.  
  461.     // if undo support is enabled, create a new action so we'll be able to undo the insertion
  462.     if (BTST(pWE->features, weFUndoSupport))
  463.     {
  464.         WEClearUndo(hWE);
  465.         if (_WENewAction(insertionOffset, insertionOffset, 0, weAKDrag, 0, hWE, &hAction) == noErr)
  466.         {
  467.             _WEPushAction(hAction);
  468.         }
  469.     }
  470.  
  471.     // count items in this drag
  472.     if ((err = CountDragItems(drag, &numDragItems)) != noErr)
  473.         goto cleanup;
  474.  
  475.     for (dragItemIndex = 1; dragItemIndex<=numDragItems; dragItemIndex++)
  476.     {
  477.         // get item reference number for current drag item
  478.         if ((err = GetDragItemReferenceNumber(drag, dragItemIndex, &dragItem)) != noErr)
  479.             goto cleanup;
  480.  
  481.         // see if this drag item contains a text flavor
  482.         err = _WEExtractFlavor(drag, dragItem, kTypeText, &hText, insertionOffset, hWE);
  483.         if (err == noErr)
  484.         {
  485.             if (!BTST(pWE->features, weFMonoStyled))
  486.             {
  487.                 // extract accompanying styles and soup, if any
  488.                 err = _WEExtractFlavor(drag, dragItem, kTypeStyles, &hStyles, insertionOffset, hWE);
  489.                 if ((err != noErr) && (err != badDragFlavorErr))
  490.                     goto cleanup;
  491. #if WASTE_OBJECTS
  492.                 err = _WEExtractFlavor(drag, dragItem, kTypeSoup, &hSoup, insertionOffset, hWE);
  493.                 if ((err != noErr) && (err != badDragFlavorErr))
  494.                     goto cleanup;
  495. #endif
  496.             }
  497.  
  498.             // any extra space added because of intelligent cut-and-paste rules will use the
  499.             // style attributes set at the insertion point
  500.             if (dragItemIndex == 1)
  501.             {
  502.                 pWE->selStart = insertionOffset;
  503.                 pWE->selEnd = insertionOffset;
  504.                 _WESynchNullStyle(hWE);
  505.             }
  506.  
  507.             // get text length
  508.             insertionLength = GetHandleSize(hText);
  509.             destEnd = insertionOffset + insertionLength;
  510.  
  511.             // insert the new text at the insertion point
  512.             HLock(hText);
  513.             err = _WEInsertText(insertionOffset, *hText, insertionLength, hWE);
  514.             _WEForgetHandle(&hText);
  515.             if (err != noErr)
  516.                 goto cleanup;
  517.  
  518.             // adjust deletion range length in undo buffer
  519.             _WEAdjustUndoRange(insertionLength, hWE);
  520.  
  521.             // apply the accompanying styles, if any
  522.             if (hStyles != nil)
  523.             {
  524.                 if ((err = _WEApplyStyleScrap(insertionOffset, destEnd,
  525.                             (StScrpHandle) hStyles, hWE)) != noErr)
  526.                     goto cleanup;
  527.                 _WEForgetHandle(&hStyles);
  528.             }
  529.  
  530. #if WASTE_OBJECTS
  531.             // apply the accompanying soup, if any
  532.             if (hSoup != nil)
  533.             {
  534.                 if ((err = _WEApplySoup(insertionOffset, hSoup, hWE)) != noErr)
  535.                     goto cleanup;
  536.                 _WEForgetHandle(&hSoup);
  537.             }
  538. #endif
  539.  
  540.             // determine whether an extra space should be added before or after the inserted text
  541.             intPasteAction = _WEIntelligentPaste(insertionOffset, destEnd, hWE);
  542.  
  543.             // add the extra space, if necessary
  544.             if (intPasteAction != weDontAddSpaces)
  545.             {
  546.                 if (intPasteAction == weAddSpaceOnLeftSide)
  547.                 {
  548.                     if ((err = _WEInsertText(insertionOffset, &space, sizeof(space), hWE)) != noErr)
  549.                         goto cleanup;
  550.  
  551.                     destEnd++;
  552.  
  553.                     // if an extra space is inserted in front of all dropped items,
  554.                     // don't count it when eventually highlighting the destination range
  555.                     if (dragItemIndex == 1)
  556.                         destStart++;
  557.                 }
  558.                 else
  559.                 {
  560.                     if ((err = _WEInsertText(destEnd, &space, sizeof(space), hWE)) != noErr)
  561.                         goto cleanup;
  562.                 }
  563.  
  564.                 insertionLength++;
  565.                 _WEAdjustUndoRange(1, hWE);
  566.             } // if extra space
  567.  
  568.         }
  569.  
  570. #if WASTE_OBJECTS
  571.         else if (err == badDragFlavorErr)
  572.         {
  573.             SInt32 objectIndex;
  574.             FlavorType objectType;
  575.             Point objectSize;
  576.             SInt16 saveUndoSupport;
  577.             SInt16 saveInhibitRecal;
  578.  
  579.             objectSize.v = 0;
  580.             objectSize.h = 0;
  581.  
  582.             // no text flavor: there must be a flavor matching one of the registered object types
  583.             objectIndex = 0;
  584.             while (_WEGetIndObjectType(objectIndex, &objectType, hWE) == noErr)
  585.             {
  586.                 err = _WEExtractFlavor(drag, dragItem, objectType, &hObjectData, insertionOffset, hWE);
  587.                 if (err == noErr)
  588.                     break; // enclosing while
  589.                 if (err != badDragFlavorErr)
  590.                     goto cleanup;
  591.                 objectIndex++;
  592.             } // while
  593.             if (err != noErr)
  594.                 goto cleanup;
  595.  
  596.             // set insertion point on first iteration (*after* extracting flavors, in case we are
  597.             // doing an intra-window move, otherwise our send proc would be confused)
  598.             if (dragItemIndex == 1)
  599.             {
  600.                 pWE->selStart = insertionOffset;
  601.                 pWE->selEnd = insertionOffset;
  602.             }
  603.  
  604.             // insert the object, but without touching undo or redrawing the text
  605.             saveUndoSupport = WEFeatureFlag(weFUndoSupport, weBitClear, hWE);
  606.             saveInhibitRecal = WEFeatureFlag(weFInhibitRecal, weBitSet, hWE);
  607.             err = WEInsertObject(objectType, hObjectData, objectSize, hWE);
  608.             WEFeatureFlag(weFUndoSupport, saveUndoSupport, hWE);
  609.             WEFeatureFlag(weFInhibitRecal, saveInhibitRecal, hWE);
  610.             if (err != noErr)
  611.                 goto cleanup;
  612.  
  613.             insertionLength = 1;
  614.             destEnd = insertionOffset + 1;
  615.             pWE->modCount--; // compensate for increment made by WEInsertObject
  616.             _WEAdjustUndoRange(1, hWE);
  617.         }
  618. #endif    // WASTE_OBJECTS
  619.  
  620.         else
  621.             goto cleanup;
  622.  
  623.         // advance insertion offset for subsequent drag items, if any
  624.         insertionOffset += insertionLength;
  625.  
  626.     } // for
  627.  
  628.     if (isMove)
  629.     {
  630.         // adjust source range
  631.         if (isBackwards)
  632.         {
  633.             delta -= pWE->textLength;
  634.             sourceStart -= delta;
  635.             sourceEnd -= delta;
  636.         }
  637.  
  638.         // extend range according to intelligent cut-and-paste rules
  639.         _WEIntelligentCut(&sourceStart, &sourceEnd, hWE);
  640.  
  641.         // if undo support is enabled, create a new action so we'll be able to undo the deletion
  642.         if (BTST(pWE->features, weFUndoSupport))
  643.         {
  644.             if (_WENewAction(sourceStart, sourceEnd, 0, weAKDrag, 0, hWE, &hAction) == noErr)
  645.             {
  646.                 _WEPushAction(hAction);
  647.             }
  648.         }
  649.  
  650.         // delete source range
  651.         delta = pWE->textLength;
  652.         if ((err = _WEDeleteRange(sourceStart, sourceEnd, hWE)) != noErr)
  653.             goto cleanup;
  654.  
  655.         // adjust destination range
  656.         if (!isBackwards)
  657.         {
  658.             delta -= pWE->textLength;
  659.             destStart -= delta;
  660.             destEnd -= delta;
  661.         }
  662.  
  663.     } // if isMove
  664.  
  665.     // select the range encompassing all items dropped
  666.     pWE->selStart = destStart;
  667.     pWE->selEnd = destEnd;
  668.  
  669.     // redraw the text
  670.     if (isMove)
  671.         if (sourceStart < destStart)
  672.             err = _WERedraw(sourceStart, destEnd, hWE);
  673.         else
  674.             err = _WERedraw(destStart, sourceEnd, hWE);
  675.     else
  676.         err = _WERedraw(destStart, destEnd, hWE);
  677.  
  678. cleanup:
  679.     // dispose of temporary handles
  680.     _WEForgetHandle(&hText);
  681.     _WEForgetHandle(&hStyles);
  682. #if WASTE_OBJECTS
  683.     _WEForgetHandle(&hSoup);
  684. #endif
  685.  
  686.     // restore the port
  687.     SetPort(savePort);
  688.  
  689.     // unlock the WE record
  690.     _WESetHandleLock((Handle) hWE, saveWELock);
  691.  
  692.     // return result code
  693.     return err;
  694.  
  695. }
  696.  
  697. pascal OSErr _WESendFlavor(FlavorType requestedType, void *dragSendRefCon, WEHandle hWE, DragReference drag)
  698. {
  699. #pragma unused(dragSendRefCon)
  700.  
  701. #if !GENERATINGCFM
  702.     SInt32 saveA5 = SetCurrentA5();    // this fixes a conflict with HoverBar
  703.                                     // (well, probably a bug in the Drag Manager)
  704. #endif
  705.  
  706.     WEPtr pWE = *hWE;
  707.     SInt32 selStart = pWE->selStart;
  708.     SInt32 selEnd = pWE->selEnd;
  709.     Handle hItem = nil;
  710.     Boolean disposeItem = true;        // dispose of item when done
  711. #if WASTE_OBJECTS
  712.     WEObjectDescHandle hObjectDesc;
  713. #endif
  714.     OSErr err;
  715.  
  716.     // allocate a temporary handle to hold a copy of the requested flavor
  717.     if ((err = _WEAllocate(0, kAllocTemp, &hItem)) != noErr)
  718.         goto cleanup;
  719.  
  720. #if WASTE_OBJECTS
  721.     // see if the selection contains an embedded object whose type matches the flavortype
  722.     if (WEGetSelectedObject(&hObjectDesc, hWE) == noErr)
  723.     {
  724.         FlavorType theType;
  725.  
  726.         if ((err = _WEStreamObject(weToDrag, &theType, &hItem, &disposeItem, hObjectDesc)) != noErr)
  727.             goto cleanup;
  728.  
  729.         // make sure theType matches the requested type
  730.         err = badDragFlavorErr;
  731.         if (theType != requestedType)
  732.             goto cleanup;
  733.     }
  734.     else
  735. #endif
  736.     {
  737.         // identify the requested flavor type as either 'TEXT', 'styl' or 'SOUP'
  738.         if (requestedType == kTypeText)
  739.             err = WECopyRange(selStart, selEnd, hItem, nil, nil, hWE);
  740.         else if (requestedType == kTypeStyles)
  741.             err = WECopyRange(selStart, selEnd, nil, hItem, nil, hWE);
  742. #if WASTE_OBJECTS
  743.         else if (requestedType == kTypeSoup)
  744.             err = WECopyRange(selStart, selEnd, nil, nil, hItem,hWE);
  745. #endif
  746.         else
  747.             err = badDragFlavorErr;
  748.  
  749.         if (err != noErr)
  750.             goto cleanup;
  751.  
  752.     }
  753.  
  754.     // set the drag flavor data
  755.     HLock(hItem);
  756.     err = SetDragItemFlavorData(drag, (ItemReference) hWE, requestedType, *hItem,
  757.             GetHandleSize(hItem), 0);
  758.     HUnlock(hItem);
  759.  
  760. cleanup:
  761.     if (disposeItem)
  762.         _WEForgetHandle(&hItem);
  763.  
  764. #if !GENERATINGCFM
  765.     SetA5(saveA5);
  766. #endif
  767.  
  768.     // return result code
  769.     return err;
  770.  
  771. }
  772.  
  773. pascal Boolean WEDraggedToTrash(DragReference drag)
  774. {
  775.  
  776.     // return true if the drop location of the specified drag is the trash
  777.  
  778.     const SInt16 bDirectoryAttr = 4;
  779.  
  780.     AEDesc dropLocation, coercedDropLocation;
  781.     CInfoPBRec pb;
  782.     FSSpecPtr pSpec;
  783.     SInt16 trashVRefNum;
  784.     SInt32 trashDirID;
  785.     Boolean draggedToTrash;
  786.  
  787.     draggedToTrash = false;
  788.     dropLocation.dataHandle = nil;
  789.     coercedDropLocation.dataHandle = nil;
  790.  
  791.     // get drop location
  792.     if (GetDropLocation(drag, &dropLocation) != noErr)
  793.         goto cleanup;
  794.  
  795.     // do nothing if dropLocation is a null descriptor
  796.     if (dropLocation.descriptorType == typeNull)
  797.         goto cleanup;
  798.  
  799.     // try to coerce the descriptor to a file system specification record
  800.     if (AECoerceDesc(&dropLocation, typeFSS, &coercedDropLocation) != noErr)
  801.         goto cleanup;
  802.  
  803.     // lock the data handle of the coerced descriptor
  804.     HLock(coercedDropLocation.dataHandle);
  805.     pSpec = *(FSSpecHandle)coercedDropLocation.dataHandle;
  806.  
  807.     // determine the directory ID of the drop location (assuming it's a folder!)
  808.     BLOCK_CLR(pb);
  809.     pb.hFileInfo.ioVRefNum = pSpec->vRefNum;
  810.     pb.hFileInfo.ioDirID = pSpec->parID;
  811.     pb.hFileInfo.ioNamePtr = pSpec->name;
  812.     if (PBGetCatInfoSync(&pb) != noErr)
  813.         goto cleanup;
  814.  
  815.     // make sure the specified file system object is really a directory
  816.     if (!BTST(pb.hFileInfo.ioFlAttrib, bDirectoryAttr))
  817.         goto cleanup;
  818.  
  819.     // find the directory ID of the trash folder
  820.     if (FindFolder(pSpec->vRefNum, kTrashFolderType, kDontCreateFolder, &trashVRefNum, &trashDirID)
  821.         != noErr)
  822.         goto cleanup;
  823.  
  824.     // compare the two directory IDs: if they're the same, the drop location is the trash
  825.     if (pb.dirInfo.ioDrDirID == trashDirID)
  826.         draggedToTrash = true;
  827.  
  828. cleanup:
  829.     // clean up
  830.     AEDisposeDesc(&dropLocation);
  831.     AEDisposeDesc(&coercedDropLocation);
  832.  
  833.     return draggedToTrash;
  834. }
  835.  
  836. pascal OSErr _WEDrag(Point mouseLoc, EventModifiers modifiers, UInt32 clickTime, WEHandle hWE)
  837. {
  838.     WEPtr pWE = *hWE;
  839.     DragReference drag = kNullDrag;
  840.     RgnHandle dragRgn = nil;
  841.     RgnHandle tmpRgn = nil;
  842.     EventRecord event;
  843.     Rect dragBounds;
  844.     Point portDelta;
  845.     GrafPtr savePort;
  846.     OSErr err;
  847. #if WASTE_OBJECTS
  848.     WEObjectDescHandle hObjectDesc;
  849. #endif
  850.  
  851.     // set up the port
  852.     GetPort(&savePort);
  853.     SetPort(pWE->port);
  854.  
  855.     // turn the cursor into an arrow
  856.     SetCursor(&qd.arrow);
  857.  
  858.     // fabricate an EventRecord for TrackDrag
  859.     event.what = mouseDown;
  860.     event.message = 0;
  861.     event.when = clickTime;
  862.     event.where = mouseLoc;
  863.     LocalToGlobal(&event.where);
  864.     event.modifiers = modifiers;
  865.  
  866.     // before seeing the dotted outline, the user must move the mouse a certain
  867.     // distance away from the initial mouse location; this allows a short click in the selection
  868.     // area to set the insertion point instead of starting a drag-and-drop sequence
  869.     err = weNoDragErr;
  870.     if (!WaitMouseMoved(event.where))
  871.         goto cleanup;
  872.  
  873.     // create a drag object
  874.     if ((err = NewDrag(&drag)) != noErr)
  875.         goto cleanup;
  876.  
  877.     WEASSERT(drag != kNullDrag, "\pZero is a valid drag reference (??)");
  878.  
  879. #if WASTE_OBJECTS
  880.     // if the selection range consists of an embedded object,
  881.     // then use its object type as flavor type
  882.     if (WEGetSelectedObject(&hObjectDesc, hWE) == noErr)
  883.     {
  884.         FlavorType theType;
  885.         Handle theData = nil;
  886.         Boolean disposeData;
  887.  
  888.         if ((err = _WEStreamObject(weToDrag, &theType, &theData, &disposeData, hObjectDesc)) != noErr)
  889.             goto cleanup;
  890.  
  891.         if ((err = AddDragItemFlavor(drag, (ItemReference) hWE, theType, nil, 0, 0)) != noErr)
  892.             goto cleanup;
  893.     }
  894.     else
  895. #endif
  896.     {
  897.  
  898.         // add a 'TEXT' flavor to the drag
  899.         if ((err = AddDragItemFlavor(drag, (ItemReference)hWE, kTypeText, nil, 0, 0)) != noErr)
  900.             goto cleanup;
  901.  
  902.         // add a 'styl' flavor to the drag
  903.         if ((err = AddDragItemFlavor(drag, (ItemReference)hWE, kTypeStyles, nil, 0, 0)) != noErr)
  904.             goto cleanup;
  905.  
  906. #if WASTE_OBJECTS
  907.         // add a 'SOUP' flavor to the drag
  908.         if ((err = AddDragItemFlavor(drag, (ItemReference)hWE, kTypeSoup, nil, 0, 0)) != noErr)
  909.             goto cleanup;
  910. #endif
  911.  
  912.     }
  913.  
  914.     // since we didn't provide the flavor data for any of the above flavors,
  915.     // we need supply a data send callback
  916.     if (_weFlavorSender == nil)
  917.         _weFlavorSender = NewDragSendDataProc(_WESendFlavor);
  918.  
  919.     if ((err = SetDragSendProc(drag, _weFlavorSender, 0)) != noErr)
  920.         goto cleanup;
  921.  
  922.     // get hilite region
  923.     dragRgn = WEGetHiliteRgn(pWE->selStart, pWE->selEnd, hWE);
  924.  
  925.     // we need just the outline of this region
  926.     tmpRgn = NewRgn();
  927.     CopyRgn(dragRgn, tmpRgn);
  928.     InsetRgn(tmpRgn, 1, 1);
  929.     DiffRgn(dragRgn, tmpRgn, dragRgn);
  930.     DisposeRgn(tmpRgn);
  931.  
  932.     // and we need it in global coordinates
  933.     portDelta.v = 0;
  934.     portDelta.h = 0;
  935.     LocalToGlobal(&portDelta);
  936.     OffsetRgn(dragRgn, portDelta.h, portDelta.v);
  937.  
  938.     // set the bounds of the drag
  939.     dragBounds = (*dragRgn)->rgnBBox;
  940.     if ((err = SetDragItemBounds(drag, (ItemReference)hWE, &dragBounds)) != noErr)
  941.         goto cleanup;
  942.  
  943.     // stash drag reference in currentDrag so WETrackDrag and WEReceiveDrag
  944.     // can tell whether a given drag originated from this WE instance
  945.     pWE->currentDrag = drag;
  946.  
  947.     // track the drag
  948.     err = TrackDrag(drag, &event, dragRgn);
  949.     pWE->currentDrag = kNullDrag;
  950.     if (err != noErr)
  951.         goto cleanup;
  952.  
  953.     // if the selection was dragged to the trash and the option key wasn't held down
  954.     // and if the instance is editable, delete the selection
  955.     if (!BTST(pWE->features, weFReadOnly))
  956.     {
  957.         if (WEDraggedToTrash(drag))
  958.         {
  959.             if (!_WEIsOptionDrag(drag))
  960.             {
  961.                 if ((err = WEDelete(hWE)) != noErr)
  962.                     goto cleanup;
  963.             }
  964.         }
  965.     }
  966.  
  967.     // clear result code
  968.     err = noErr;
  969.  
  970. cleanup:
  971.     // dispose of the drag
  972.     if (drag != kNullDrag)
  973.     {
  974.         DisposeDrag(drag);
  975.     }
  976.  
  977.     // dispose of the drag region
  978.     if (dragRgn != nil)
  979.         DisposeRgn(dragRgn);
  980.  
  981.     // restore the port
  982.     SetPort(savePort);
  983.  
  984.     // return result code
  985.     return err;
  986. }
  987.  
  988. #if WASTE_IC_SUPPORT
  989.  
  990. pascal void _WEResolveURL(EventModifiers modifiers, SInt32 urlStart, SInt32 urlEnd, WEHandle hWE)
  991. {
  992.     WEPtr pWE = *hWE;            // assume WE record is already locked
  993.     Str255 hint;
  994.     FourCharCode signature;
  995.     ProcessSerialNumber psn;
  996.     ProcessInfoRec info;
  997.     ICInstance inst;
  998.     ICError err;
  999.     SInt32 junkLong;
  1000.     Handle hURL = nil;
  1001.     Boolean saveTextLock;
  1002.  
  1003.     // get the hint string IC will use to parse slack URLs
  1004.     hint[0] = 0;
  1005.     if (pWE->hURLHint != nil)
  1006.     {
  1007.         BlockMoveData(*pWE->hURLHint, hint, StrLength(*pWE->hURLHint) + 1);
  1008.     }
  1009.  
  1010.     // find out the signature of the current process
  1011.     signature = '\?\?\?\?';
  1012.     psn.highLongOfPSN = 0;
  1013.     psn.lowLongOfPSN = 0;
  1014.     if (GetCurrentProcess(&psn) == noErr)
  1015.     {
  1016.         BLOCK_CLR(info);
  1017.         info.processInfoLength = sizeof(info);
  1018.         if (GetProcessInformation(&psn, &info) == noErr)
  1019.             signature = info.processSignature;
  1020.     }
  1021.  
  1022.     if (ICStart(&inst, signature) == noErr)
  1023.     {
  1024.         if (ICFindConfigFile(inst, 0, nil) == noErr)
  1025.         {
  1026.             saveTextLock = _WESetHandleLock(pWE->hText, true);
  1027.  
  1028.             // cmd + option click just highlights the URL,
  1029.             // without actually resolving it
  1030.             if (modifiers & optionKey)
  1031.             {
  1032.                 hURL = NewHandle(0);
  1033.                 err = ICParseURL(inst, hint, *pWE->hText, WEGetTextLength(hWE), &urlStart, &urlEnd, hURL);
  1034.                 _WEForgetHandle(&hURL);
  1035.             }
  1036.             else
  1037.             {
  1038.                 err = ICLaunchURL(inst, hint, *pWE->hText, WEGetTextLength(hWE), &urlStart, &urlEnd);
  1039.             }
  1040.             _WESetHandleLock(pWE->hText, saveTextLock);
  1041.             WESetSelection(urlStart, urlEnd, hWE);
  1042.  
  1043.             // flash selection if successful (unless option key was down)
  1044.             if ((err == noErr) && !(modifiers & optionKey))
  1045.             {
  1046.                 Delay(5, &junkLong);
  1047.                 WEDeactivate(hWE);
  1048.                 Delay(5, &junkLong);
  1049.                 WEActivate(hWE);
  1050.             }
  1051.         }
  1052.         ICStop(inst);
  1053.     }
  1054. }
  1055.  
  1056. #endif    // WASTE_IC_SUPPORT
  1057.  
  1058. pascal void WEClick(Point mouseLoc, EventModifiers modifiers, UInt32 clickTime, WEHandle hWE)
  1059. {
  1060.     WEPtr pWE;
  1061.     LongPt thePoint;
  1062.     SInt32 offset, anchor;
  1063.     SInt32 rangeStart, rangeEnd;
  1064.     WEEdge edge;
  1065.     Boolean isMultipleClick;
  1066.     Boolean saveWELock;
  1067. #if WASTE_OBJECTS
  1068.     WEObjectDescHandle hObjectDesc;
  1069. #endif
  1070. #if WASTE_IC_SUPPORT
  1071.     SInt32 urlStart, urlEnd;
  1072. #endif
  1073.  
  1074.     // stop any ongoing inline input session
  1075.     WEStopInlineSession(hWE);
  1076.  
  1077.     // lock the WE record
  1078.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1079.     pWE = *hWE;
  1080.  
  1081. #if WASTE_IC_SUPPORT
  1082.     // remember the selection range before the click
  1083.     urlStart = pWE->selStart;
  1084.     urlEnd = pWE->selEnd;
  1085. #endif
  1086.  
  1087.     // hide the caret if it's showing
  1088.     if (BTST(pWE->flags, weFCaretVisible))
  1089.         _WEBlinkCaret(hWE);
  1090.  
  1091.     // find click offset
  1092.     WEPointToLongPoint(mouseLoc, &thePoint);
  1093.     offset = WEGetOffset(&thePoint, &edge, hWE);
  1094.  
  1095.     // determine whether this click is part of a sequence
  1096.     // a single click inside an object selects it, so it's like a double click in a word
  1097.     isMultipleClick = ((clickTime < pWE->clickTime + GetDoubleClickTime()) && (offset == pWE->clickLoc));
  1098.  
  1099.     // remember click time, click offset and edge value
  1100.     pWE->clickTime = clickTime;
  1101.     pWE->clickLoc = offset;
  1102.     pWE->clickEdge = edge;
  1103.  
  1104. #if WASTE_OBJECTS
  1105.     // when selected, embedded objects can intercept mouse clicks
  1106.     if (WEGetSelectedObject(&hObjectDesc, hWE) == noErr)
  1107.         if (_WEOffsetInRange(offset, edge, pWE->selStart, pWE->selEnd))
  1108.             if (_WEClickObject(mouseLoc, modifiers + isMultipleClick, clickTime, hObjectDesc))
  1109.                     goto cleanup;
  1110. #endif
  1111.  
  1112.     if ((modifiers & shiftKey) == 0)
  1113.     {
  1114.  
  1115.         // is this click part of a sequence or is it a single click?
  1116.         if (isMultipleClick)
  1117.         {
  1118.             pWE->clickCount++;
  1119.  
  1120.             // a double (triple) click creates an anchor-word (anchor-line)
  1121.             if (pWE->clickCount > 1)
  1122.                 WEFindLine(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
  1123.             else
  1124.                 WEFindWord(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
  1125.  
  1126.             offset = pWE->anchorStart;
  1127.         }
  1128.         else
  1129.         {
  1130.             // single-click
  1131.             // if the Drag Manager is available and the click went in the selection range,
  1132.             // this click may be the beginning of a drag gesture
  1133.             if (BTST(pWE->flags, weFHasDragManager) && BTST(pWE->features, weFDragAndDrop))
  1134.                 if (_WEOffsetInRange(offset, edge, pWE->selStart, pWE->selEnd))
  1135.                     if (_WEDrag(mouseLoc, modifiers, clickTime, hWE) != weNoDragErr)
  1136.                         goto cleanup;
  1137.  
  1138.             pWE->clickCount = 0;
  1139.             anchor = offset;
  1140.         }
  1141.     }
  1142.     else
  1143.     {
  1144.  
  1145.         // if the shift key was down, use the old anchor offset found with the previous click
  1146.         anchor = BTST(pWE->flags, weFAnchorIsEnd) ? pWE->selEnd : pWE->selStart;
  1147.     }
  1148.  
  1149.     // set the weFMouseTracking bit while we track the mouse
  1150.     BSET(pWE->flags, weFMouseTracking);
  1151.  
  1152.     // MOUSE TRACKING LOOP
  1153.     do
  1154.     {
  1155.  
  1156.         // get text offset corresponding to mouse position
  1157.         WEPointToLongPoint(mouseLoc, &thePoint);
  1158.         offset = WEGetOffset(&thePoint, &edge, hWE);
  1159.  
  1160.         // if we're selecting words or lines, pin offset to a word or line boundary
  1161.         if (pWE->clickCount > 0)
  1162.         {
  1163.             if (pWE->clickCount > 1)
  1164.                 WEFindLine(offset, edge, &rangeStart, &rangeEnd, hWE);
  1165.             else
  1166.                 WEFindWord(offset, edge, &rangeStart, &rangeEnd, hWE);
  1167.  
  1168.             // choose the word/line boundary and the anchor that are farthest away from each other
  1169.             if (offset > pWE->anchorStart)
  1170.             {
  1171.                 anchor = pWE->anchorStart;
  1172.                 offset = rangeEnd;
  1173.             }
  1174.             else
  1175.             {
  1176.                 offset = rangeStart;
  1177.                 anchor = pWE->anchorEnd;
  1178.             }
  1179.         }
  1180.         else
  1181.         {
  1182.             // if the point is in the middle of an object, the selection should include it
  1183.             if (edge == kObjectEdge)
  1184.                 offset++;
  1185.         }
  1186.  
  1187.         // set the selection range from anchor point to current offset
  1188.         WESetSelection(anchor, offset, hWE);
  1189.  
  1190.         // call the click loop callback, if any
  1191.         if (pWE->clickLoop != nil)
  1192.             if (!CallWEClickLoopProc(hWE, pWE->clickLoop))
  1193.                 break;
  1194.  
  1195.         // update mouse position
  1196.         GetMouse(&mouseLoc);
  1197.  
  1198.     } while(WaitMouseUp());
  1199.  
  1200.     // clear the weFMouseTracking bit
  1201.     BCLR(pWE->flags, weFMouseTracking);
  1202.  
  1203.     // redraw the caret immediately if the selection range is empty
  1204.     if (anchor == offset)
  1205.         _WEBlinkCaret(hWE);
  1206.  
  1207. #if WASTE_IC_SUPPORT
  1208.     if (modifiers & cmdKey)
  1209.     {
  1210.         // command+clicking a URL tries to resolve it
  1211.         // we normally ask IC to parse the text surrounding the clicked point,
  1212.         // but if a selection already existed prior to the click, we pass
  1213.         // that to IC rather than forcing a re-parse
  1214.         if ((anchor != offset) || (anchor < urlStart) || (anchor > urlEnd))
  1215.         {
  1216.             urlStart = anchor;
  1217.             urlEnd = offset;
  1218.         }
  1219.         _WEResolveURL(modifiers, urlStart, urlEnd, hWE);
  1220.     }
  1221. #endif
  1222.  
  1223. cleanup:
  1224.     // unlock the WE record
  1225.     _WESetHandleLock((Handle) hWE, saveWELock);
  1226.  
  1227. }
  1228.  
  1229. pascal UInt16 WEGetClickCount(WEHandle hWE)
  1230. {
  1231.     return (*hWE)->clickCount;
  1232. }
  1233.